﻿using System.Security.Claims;
using ApiLib.AppStart;
using ApiLib.Models;
using Furion;
using Microsoft.AspNetCore.Http;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver;
using MongoDB.Driver.Linq;

namespace ApiLib.Utils;

/// <summary>
/// 
/// </summary>
public static class MongoUtil
{
    /// <summary>
    /// 客户端
    /// </summary>
    public static MongoClient Client;

    /// <summary>
    /// 数据库
    /// </summary>
    public static IMongoDatabase Db;

    /// <summary>
    /// 使用mongodb存储日志
    /// </summary>
    public static bool UseMongo =>
        GlobalConfig.AppConfig != null && GlobalConfig.AppConfig.MongoHost.IsNotNullOrEmpty();

    /// <summary>
    /// 初始化
    /// </summary>
    public static void Init()
    {
        if (!UseMongo) return;

        var mongoUrl = GlobalConfig.AppConfig?.MongoHost ?? "localhost:27017";
        Client = new MongoClient($"mongodb://{mongoUrl}")
        {
            Settings =
            {
                ConnectTimeout = TimeSpan.FromSeconds(GlobalConfig.AppConfig?.MongoTimeout??10)
            }
        };
        Db = Client.GetDatabase(GlobalConfig.AppConfig?.MongoDb ?? "logs");
        WebUtil.LogSys("初始化Mongo成功");
    }

    #region 通用增删改查

    /// <summary>
    /// 日志
    /// </summary>
    /// <param name="log"></param>
    /// <param name="type"></param>
    /// <returns></returns>
    public static void Log(string log, LogType type = LogType.Normal)
    {
        Insert(new MongoLogInfo(type, log));
    }

    /// <summary>
    /// 异常日志
    /// </summary>
    /// <param name="log"></param>
    /// <returns></returns>
    public static void LogExp(string log)
    {
        Insert(new MongoLogInfo(LogType.Exp, log));
    }

    /// <summary>
    /// sql日志
    /// </summary>
    /// <param name="log"></param>
    /// <returns></returns>
    public static void LogSql(string log)
    {
        Insert(new MongoLogInfo(LogType.Sql, log));
    }

    /// <summary>
    /// 系统日志
    /// </summary>
    /// <param name="log"></param>
    /// <returns></returns>
    public static void LogSys(string log)
    {
        Insert(new MongoLogInfo(LogType.Sys, log));
    }

    /// <summary>
    /// 新增单个实体模型
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="entity"></param>
    public static void Insert<T>(T entity)
    {
        if (!UseMongo) return;
        // var doc = entity as BsonDocument ?? entity.ToBsonDocument();
        Db.GetCollection<T>(typeof(T).Name).InsertOne(entity);
    }

    /// <summary>
    /// 删除日志
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="objId"></param>
    [Obsolete]
    public static void Delete<T>(string objId)
    {
        var filterDefinition = Builders<MongoLogInfo>.Filter.Eq("_id", objId);
        Db.GetCollection<T>(typeof(T).Name).DeleteOne(objId);
    }

    /// <summary>
    /// 删除日志
    /// </summary>
    /// <param name="before"></param>
    public static void DeleteLog(DateTime before)
    {
        var filterDefinition = Builders<MongoLogInfo>.Filter.Lt(nameof(MongoLogInfo.Time), before);
        Db.GetCollection<MongoLogInfo>(nameof(MongoLogInfo)).DeleteMany(filterDefinition);
    }

    /// <summary>
    /// 查询数据
    /// </summary>
    /// <param name="param"></param>
    /// <returns></returns>
    public static ListResponse<MongoLogInfo> SearchLog(MongoLogParam param)
    {
        var queryable = Db
            .GetCollection<MongoLogInfo>(nameof(MongoLogInfo))
            .AsQueryable();
        var iQueryable = param.InitWhere(queryable);
        var count = iQueryable.Count();

        var collection = iQueryable.OrderByDescending(info => info.Time)
            .Skip((param.PageIndex - 1) * param.PageSize)
            .Take(param.PageSize)
            .ToList();
        return new ListResponse<MongoLogInfo>()
        {
            TotalCount = int.Parse(count.ToString()),
            List = collection
        };
    }

    #endregion
}

/// <summary>
/// 日志类型
/// </summary>
public enum LogType
{
    /// <summary>
    /// 普通日志
    /// </summary>
    Normal,

    /// <summary>
    /// 异常日志
    /// </summary>
    Exp,

    /// <summary>
    /// SQL日志
    /// </summary>
    Sql,

    /// <summary>
    /// 系统日志
    /// </summary>
    Sys,

    /// <summary>
    /// 请求日志
    /// </summary>
    Req
}

/// <summary>
/// mongo日志对象
/// </summary>
[BsonIgnoreExtraElements]
public class MongoLogInfo
{
    /// <summary>
    /// 
    /// </summary>
    public string TraceId { get; set; }

    /// <summary>
    /// 用户
    /// </summary>
    public string UserSid { get; set; }

    /// <summary>
    /// 服务
    /// </summary>
    public string ServeName { get; set; }

    /// <summary>
    /// normal,exp,sql,sys
    /// </summary>
    public string Type { get; set; }

    /// <summary>
    /// Ip
    /// </summary>
    public string Ip { get; set; }

    /// <summary>
    /// 路径
    /// </summary>
    public string Path { get; set; }

    /// <summary>
    /// 时间
    /// </summary>
    public DateTime Time { get; set; }

    /// <summary>
    /// 日志
    /// </summary>
    public string Log { get; set; }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="type"></param>
    /// <param name="log"></param>
    public MongoLogInfo(LogType type, string log)
    {
        TraceId = App.GetTraceId();
        UserSid = App.User?.FindFirstValue(ClaimTypes.Sid) ?? "";
        ServeName = GlobalConfig.ServeName;
        Ip = App.HttpContext?.GetRemoteIpAddressToIPv4() ?? "";
        Path = App.HttpContext?.Request.Path ?? "";
        Time = CommonUtil.LocalDateTime();
        Type = type.ToString();
        Log = log;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="log"></param>
    public MongoLogInfo(string log) : this(LogType.Normal, log)
    {
    }
}

/// <summary>
/// 查询参数
/// </summary>
public class MongoLogParam : PageParam
{
    /// <summary>
    /// 
    /// </summary>
    public string ObjectId { get; set; } = "";

    /// <summary>
    /// 
    /// </summary>
    public string TraceId { get; set; } = "";

    /// <summary>
    /// 用户
    /// </summary>
    public string UserSid { get; set; } = "";

    /// <summary>
    /// 服务
    /// </summary>
    public string ServeName { get; set; } = "";

    /// <summary>
    /// normal,exp,sql,sys
    /// </summary>
    public string Type { get; set; } = "";

    /// <summary>
    /// Ip
    /// </summary>
    public string Ip { get; set; } = "";

    /// <summary>
    /// 路径
    /// </summary>
    public string Path { get; set; } = "";

    /// <summary>
    /// 开始时间
    /// </summary>
    public DateTime StartTime { get; set; }

    /// <summary>
    /// 结束时间
    /// </summary>
    public DateTime EndTime { get; set; }

    /// <summary>
    /// 初始化where条件
    /// </summary>
    /// <param name="queryable"></param>
    /// <returns></returns>
    public IQueryable<MongoLogInfo> InitWhere(IMongoQueryable<MongoLogInfo> queryable)
    {
        return queryable
            // .WhereIf(!string.IsNullOrEmpty(ObjectId), info => info.Id == ObjectId)
            .WhereIf(!string.IsNullOrEmpty(TraceId), info => info.TraceId == TraceId)
            .WhereIf(!string.IsNullOrEmpty(UserSid), info => info.UserSid == UserSid)
            .WhereIf(!string.IsNullOrEmpty(ServeName), info => info.ServeName == ServeName)
            .WhereIf(!string.IsNullOrEmpty(Type), info => info.Type == Type)
            .WhereIf(!string.IsNullOrEmpty(Ip), info => info.Ip == Ip)
            .WhereIf(!string.IsNullOrEmpty(Path), info => info.Path.Contains(Path))
            .WhereIf(StartTime != DateTime.MinValue && EndTime != DateTime.MinValue,
                info => info.Time >= StartTime && info.Time <= EndTime);
    }

    /// <summary>
    /// 初始化filter
    /// </summary>
    /// <returns></returns>
    public FilterDefinition<MongoLogInfo> InitFilter()
    {
        var definitions = new List<FilterDefinition<MongoLogInfo>>();

        if (!string.IsNullOrEmpty(TraceId))
        {
            definitions.Add(Builders<MongoLogInfo>.Filter.Eq(nameof(MongoLogInfo.TraceId), TraceId));
        }

        if (!string.IsNullOrEmpty(UserSid))
        {
            definitions.Add(Builders<MongoLogInfo>.Filter.Eq(nameof(MongoLogInfo.UserSid), UserSid));
        }

        if (!string.IsNullOrEmpty(ServeName))
        {
            definitions.Add(Builders<MongoLogInfo>.Filter.Eq(nameof(MongoLogInfo.ServeName), ServeName));
        }

        if (!string.IsNullOrEmpty(Type))
        {
            definitions.Add(Builders<MongoLogInfo>.Filter.Eq(nameof(MongoLogInfo.Type), Type));
        }

        if (!string.IsNullOrEmpty(Ip))
        {
            definitions.Add(Builders<MongoLogInfo>.Filter.Eq(nameof(MongoLogInfo.Ip), Ip));
        }

        if (!string.IsNullOrEmpty(Path))
        {
            definitions.Add(Builders<MongoLogInfo>.Filter
                .Where(doc => doc.Path.Contains(Path)));
        }

        if (StartTime != DateTime.MinValue && EndTime != DateTime.MinValue)
        {
            definitions.Add(Builders<MongoLogInfo>.Filter
                .Gte(nameof(MongoLogInfo.Time), StartTime));
            definitions.Add(Builders<MongoLogInfo>.Filter
                .Lte(nameof(MongoLogInfo.Time), EndTime));
        }

        return Builders<MongoLogInfo>.Filter.And(definitions);
    }
}